package ldh.im.fxbase.service;

import ldh.im.fxbase.util.ExceptionUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.sound.sampled.*;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.function.BiConsumer;
import java.util.function.Consumer;

public class AudioSocketService extends AudioService {

    private static final Logger LOGGER = LoggerFactory.getLogger(AudioSocketService.class);

    private static final AudioFormat.Encoding AUDIO_ENCODE = AudioFormat.Encoding.PCM_SIGNED;
    private SourceDataLine sourceDataLine;
    private TargetDataLine targetDataLine;
    private ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
    private BiConsumer<byte[], Integer> biConsumer;
    private byte[] fragment = null;
    private long startTime = 0;
    private volatile boolean pause = false;

    public AudioSocketService() {}

    public AudioSocketService(BiConsumer<byte[], Integer> biConsumer) {
        this.biConsumer = biConsumer;
    }

    // 录制声音
    public void startStream() {
        try {
            AudioFormat audioFormat = getAudioFormat();
            DataLine.Info dataLineInfo = new DataLine.Info(SourceDataLine.class, audioFormat);
            sourceDataLine = (SourceDataLine) AudioSystem.getLine(dataLineInfo);
            sourceDataLine.open(audioFormat);
            sourceDataLine.start();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // 播放声音
    public void playAudio(byte[] bytes, int length) {
        System.out.println("isAcitve:" + sourceDataLine.isActive());
        if (sourceDataLine != null) {
            sourceDataLine.write(bytes, 0, length);
        }
    }

    public void closeAudio() {
        if (sourceDataLine != null) {
            sourceDataLine.stop();
            sourceDataLine.close();
        }
    }

    public void closePlay() {
        if (targetDataLine != null) {
            targetDataLine.stop();
            targetDataLine.close();
        }
    }

    public void close() {
        closeAudio();
        closePlay();
        executorService.shutdownNow();
    }

    public AudioFormat getAudioFormat() {
        float sampleRate = 8000;     // 8000,11025,16000,22050,44100
        int sampleSizeInBits = 16;    // 8,16
        int channels = 1;             // 1,2
        int frameSize = 2;
        boolean signed = true;        // true,false
        boolean bigEndian = false;    // true,false
        return new AudioFormat(AUDIO_ENCODE, sampleRate, sampleSizeInBits, channels, frameSize, sampleRate,  bigEndian);
    }

    @Override
    public void playRecord(String file, Consumer<Void> consumer) {
        // do nothing
    }

    @Override
    public void startRecord() {
        ExceptionUtil.catchException(()->{
            // 获得指定的音频格式
            audioFormat = getAudioFormat();
            int numChannels = audioFormat.getChannels();
            int sampleRate = (int) audioFormat.getSampleRate();
            int audioBufferSize = sampleRate * numChannels;
            fragment = new byte[audioBufferSize];
            DataLine.Info dataLineInfo = new DataLine.Info(TargetDataLine.class, audioFormat);
            targetDataLine = (TargetDataLine) AudioSystem.getLine(dataLineInfo);
            while(targetDataLine == null) {
                Thread.sleep(10);
                targetDataLine = (TargetDataLine) AudioSystem.getLine(dataLineInfo);
            }
            startTime = System.currentTimeMillis();
            recordTimeProperty.setValue(0);
            targetDataLine.open(getAudioFormat());
            targetDataLine.start();
            executorService.scheduleWithFixedDelay(getAudioRecordThread(), 0, 1000/25, TimeUnit.MILLISECONDS);
            return null;
        }, "获取音频数据");
    }

    public Runnable getAudioRecordThread() {
        return ()-> {
            try {
                if(pause) return;
                int length = targetDataLine.read(fragment, 0, fragment.length);
                long time = System.currentTimeMillis() - startTime;
                recordTimeProperty.setValue(time);
                biConsumer.accept(fragment, length);
            } catch (Exception e) {
                LOGGER.error("获取语音数据失败", e);
            }
        };
    }

    public void pause() {
        this.pause = true;
    }

    public void run() {
        this.pause = false;
    }
}

